package com.frank.redis.config;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.io.Serializable;
import java.time.Duration;

/**
 * @author 小石潭记
 * @date 2020/10/19 9:10
 * @Description:  redis的配置，端口和主机ip都是有默认值yml文件里面暂时未配置
 */

@Configuration
@EnableCaching
public class RedisConfig {

    @Value("${spring.application.name}")
    private String applicationName;

    @Value("${spring.cache.couchbase.expiration}")
    private Duration expireDuration;

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
        .computePrefixWith(cacheName ->
                new StringBuilder()
                        .append(applicationName)
                        .append(":")
                        .append(cacheName)
                        .toString())
        // 设置时间15s
//        .entryTtl(Duration.ofSeconds(60))
        .entryTtl(expireDuration)
        .serializeValuesWith(valueSerializer())
        .serializeKeysWith(keySerializer());
        return RedisCacheManager
                .builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
                .cacheDefaults(redisCacheConfiguration)
                .build();
    }

/**
     *  如果直接返回new GenericJackson2JsonRedisSerializer()
     *  则会报错，redis存储的日期读取失败，需要objectValueSerializer里面手动设置一下objectMapper
     * @return
     */

    private RedisSerializationContext.SerializationPair<Object> valueSerializer() {
        return RedisSerializationContext.SerializationPair.fromSerializer(objectValueSerializer());
//        return RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer());
    }

    private RedisSerializationContext.SerializationPair<String> keySerializer() {
        return RedisSerializationContext.SerializationPair.fromSerializer(stringValueSerializer());
    }

/**
     * 如果不设置下面的objectMapper，则会报以下错
     * com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of
     * `java.time.LocalDate` (no Creators, like default construct, exist): cannot deserialize from
     * Object value (no delegate- or property-based Creator)
     * @return
     */
    @Bean
    public RedisSerializer objectValueSerializer() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.findAndRegisterModules();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        return new GenericJackson2JsonRedisSerializer(objectMapper);
    }

    // 这里配置的controller会默认使用，redis里面不（会 new GenericJackson2JsonRedisSerializer()）
    @Bean
    @Primary
    public ObjectMapper customObjectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.findAndRegisterModules();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        // 设置返回对象属性为空时  不展示
        // objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        objectMapper.registerModule(new JavaTimeModule());
        return objectMapper;
    }

    @Bean
    public RedisTemplate<Object, Serializable> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<Object, Serializable> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(stringValueSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        redisTemplate.setConnectionFactory(connectionFactory);
        redisTemplate.setDefaultSerializer(objectValueSerializer());
        redisTemplate.setStringSerializer(stringValueSerializer());
        return redisTemplate;
    }

    /*@Bean("primaryRedisTemplate")
    @Primary
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setDefaultSerializer(objectValueSerializer());
        redisTemplate.setStringSerializer(stringValueSerializer());
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        return redisTemplate;
    }*/

    @Bean("stringRedisSerializer")
    public RedisSerializer stringValueSerializer() {
        return new StringRedisSerializer();
    }

}
